fix(ai): work around httpx NO_PROXY IPv6 CIDR crash#463
fix(ai): work around httpx NO_PROXY IPv6 CIDR crash#463dingyufei615 merged 1 commit intoUsagi-org:masterfrom
Conversation
httpx <= 0.28.1 wraps NO_PROXY IPv6 entries including the CIDR mask inside brackets (e.g. `[::1/128]`), which its URL parser rejects as `Invalid port: ':1'`. This is triggered by OrbStack/Docker Desktop injecting `NO_PROXY=localhost,127.0.0.0/8,::1/128` into containers. Strip the `/prefix` from IPv6 CIDR entries before initializing AsyncOpenAI. This is safe because httpx doesn't support CIDR range matching anyway — it only does exact-host comparison. Upstream fix: encode/httpx#3741 (pending merge)
There was a problem hiding this comment.
Code Review
This pull request introduces a _sanitize_no_proxy_env utility to strip CIDR prefixes from IPv6 entries in the NO_PROXY environment variables, addressing a known issue in httpx. It also includes comprehensive unit tests for this logic. Feedback suggests optimizing the IPv6 detection logic to avoid unnecessary exception handling for IPv4 addresses and cleaning up unused variables.
| if "/" in part: | ||
| host, _, prefix = part.partition("/") | ||
| try: | ||
| ipaddress.IPv6Address(host) | ||
| cleaned.append(host) | ||
| changed = True | ||
| continue | ||
| except ValueError: | ||
| pass | ||
| cleaned.append(part) |
There was a problem hiding this comment.
Optimization: Adding a check for : before attempting to parse the host as an IPv6 address can avoid the overhead of the try/except block for IPv4 CIDRs (e.g., 127.0.0.0/8) and other non-IPv6 entries in NO_PROXY. Additionally, the prefix variable is unused and can be replaced with _ to follow Python conventions.
| if "/" in part: | |
| host, _, prefix = part.partition("/") | |
| try: | |
| ipaddress.IPv6Address(host) | |
| cleaned.append(host) | |
| changed = True | |
| continue | |
| except ValueError: | |
| pass | |
| cleaned.append(part) | |
| if "/" in part and ":" in part: | |
| host, _, _ = part.partition("/") | |
| try: | |
| ipaddress.IPv6Address(host) | |
| cleaned.append(host) | |
| changed = True | |
| continue | |
| except ValueError: | |
| pass | |
| cleaned.append(part) |
|
thanks bro |
Problem
OrbStack / Docker Desktop automatically injects proxy environment variables into containers:
httpx <= 0.28.1's
get_environment_proxies()wraps IPv6 CIDR entries fromNO_PROXYinside brackets including the CIDR mask (e.g.all://[::1/128]), causing the URL parser to misinterpret/128as a port and throwInvalid port: ':1'.This crashes
AsyncOpenAIclient initialization, making AI analysis completely unavailable.Root Cause
In httpx
_utils.get_environment_proxies():is_ipv6_hostnamecorrectly identifies IPv6 viasplit("/")[0], but the URL construction includes the CIDR mask inside brackets.URLPatternthen parses[::1/128]and treats/128]as part of the authority, ultimately failing onint(":1").Upstream fix PR (encode/httpx#3741) has passing CI but remains unmerged. httpx has not released for over 16 months.
Fix
Call
_sanitize_no_proxy_env()before creatingAsyncOpenAIinAIClient._initialize_client. This strips CIDR prefix lengths from IPv6 entries inNO_PROXY/no_proxy(e.g.::1/128->::1).10.0.0.0/8) are preservedTests
5 new unit tests covering: IPv6 CIDR stripping, case variants (
NO_PROXY/no_proxy), IPv4 CIDR preservation, missing env vars, and both keys present simultaneously.Verified in container:
AIClientinitializes successfully withNO_PROXY=localhost,127.0.0.0/8,::1/128.